#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/core/make_raw_handle.h>
#include <vex/core/allocate_raw_handle.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_properties.h>
#include <vex.tests/mstest/tests.core/user_contract.h>
#include <vex.tests/mstest/tests.core/user_base.h>
#include <vex.tests/mstest/tests.core/user_type_traits.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests { namespace make_raw_handle {

    namespace vc = vex::core;
    namespace vcc = vex::core::contract;
    namespace vci = vex::core::implementation;

    TEST_CLASS(make_raw_handle)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.core.make_raw_handle")
        END_TEST_CLASS_ATTRIBUTE();
        VEX_TESTS_TRACKED_MEM_CHECK();
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_START();
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
        }

        template<class THandle, class TOwnershipTag>
        void release(THandle* p_handle, TOwnershipTag)
        {
            if (p_handle) {
                p_handle->release();
            }
        }

        template<class THandle>
        void release(THandle* p_handle, vex::core::unmanaged_tag)
        {
            if (p_handle) {
                delete p_handle;
            }
        }

        void make_raw_handle_delivers_a_valid_handle_impl()
        {
            if (auto c = vex::core::make_raw_handle<user_base>()) {
                handle_properties::verify_sharing(c, vc::shared_synchronized_tag());

                Assert::AreEqual(user_base_traits::get<int>(user_base::get_defaults()),
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, vc::shared_synchronized_tag());
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template<class TOwnership>
        void make_raw_handle_ext_delivers_a_valid_handle_impl()
        {
            if (auto c = vc::make_raw_handle_ext<user_base, TOwnership>()) 
            {
                handle_properties::verify_sharing(c, TOwnership::ownership_type());

                Assert::AreEqual(user_base_traits::get<int>(user_base::get_defaults()),
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, TOwnership::ownership_type());
            } else
                Assert::Fail(L"Handle could not be allocated!");
        }

        template <class TArg1>
        void make_raw_handle_delivers_a_valid_handle_impl(TArg1&& p_arg1)
        {
            if (auto c = vc::make_raw_handle<user_base>( 
                std::forward<TArg1>(p_arg1))) 
            {
                handle_properties::verify_sharing(c, vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, vc::shared_synchronized_tag());
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template<class TOwnership, class TArg1>
        void make_raw_handle_ext_delivers_a_valid_handle_impl(TArg1&& p_arg1)
        {
            if (auto c = vc::make_raw_handle_ext<user_base, TOwnership>(
                std::forward<TArg1>(p_arg1))) 
            {
                handle_properties::verify_sharing(c, TOwnership::ownership_type());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, TOwnership::ownership_type());
            } else
                Assert::Fail(L"Handle could not be allocated!");
        }


        template <class TArg1, class TArg2>
        void make_raw_handle_delivers_a_valid_handle_impl(TArg1 && p_arg1, TArg2 && p_arg2)
        {
            if (auto c = vc::make_raw_handle<user_base>( 
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2))) 
            {
                handle_properties::verify_sharing(c, vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, vc::shared_synchronized_tag());
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template<class TOwnership, class TArg1, class TArg2>
        void make_raw_handle_ext_delivers_a_valid_handle_impl(TArg1&& p_arg1, TArg2&& p_arg2)
        {
            if (auto c = vc::make_raw_handle_ext<user_base, TOwnership>(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2))) 
            {
                handle_properties::verify_sharing(c, TOwnership::ownership_type());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, TOwnership::ownership_type());
            } else
                Assert::Fail(L"Handle could not be allocated!");
        }

        template <class TArg1, class TArg2, class TArg3>
        void make_raw_handle_delivers_a_valid_handle_impl(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3)
        {
            if (auto c = vc::make_raw_handle<user_base>( 
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3))) 
            {
                handle_properties::verify_sharing(c, vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(std::string(p_arg3),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, vc::shared_synchronized_tag());
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template<class TOwnership, class TArg1, class TArg2, class TArg3>
        void make_raw_handle_ext_delivers_a_valid_handle_impl(TArg1&& p_arg1, TArg2&& p_arg2, TArg3&& p_arg3)
        {
            if (auto c = vc::make_raw_handle_ext<user_base, TOwnership>(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2),
                std::forward<TArg3>(p_arg3))) 
            {
                handle_properties::verify_sharing(c, TOwnership::ownership_type());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(std::string(p_arg3),
                    std::string(c->get_value(alias<char const*>()))); 

                this->release(c, TOwnership::ownership_type());
            } else
                Assert::Fail(L"Handle could not be allocated!");
        }

        TEST_METHOD(make_raw_handle_delivers_a_valid_handle_for_nullary_form)
        {
            this->make_raw_handle_delivers_a_valid_handle_impl();
        }

        TEST_METHOD(make_raw_handle_delivers_a_valid_handle_for_unary_form)
        {
            this->make_raw_handle_delivers_a_valid_handle_impl(42);

            int t_lvalue = 42;
            this->make_raw_handle_delivers_a_valid_handle_impl(t_lvalue);
        }

        TEST_METHOD(make_raw_handle_delivers_a_valid_handle_for_binary_form)
        {
            this->make_raw_handle_delivers_a_valid_handle_impl(42, 24.1);

            int t_i = 42;
            double t_d = 24.1;
            this->make_raw_handle_delivers_a_valid_handle_impl(t_i, t_d);
        }

        TEST_METHOD(make_raw_handle_delivers_a_valid_handle_for_ternary_form)
        {
            this->make_raw_handle_delivers_a_valid_handle_impl(42, 24.1, "josephine");

            int t_i = 42;
            double t_d = 24.1;
            std::string t_s = "josephine";
            this->make_raw_handle_delivers_a_valid_handle_impl(t_i, t_d, t_s);
        }

        TEST_METHOD(make_raw_handle_ext_delivers_a_valid_handle_for_nullary_form)
        {
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared>();
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared_synchronized>();
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unique>();
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unmanaged>();
        }

        TEST_METHOD(make_raw_handle_ext_delivers_a_valid_handle_for_unary_form)
        {
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared>(42);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared_synchronized>(42);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unique>(42);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unmanaged>(42);
        }

        TEST_METHOD(make_raw_handle_ext_delivers_a_valid_handle_for_binary_form)
        {
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared>(42, 24.1);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::shared_synchronized>(42, 24.1);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unique>(42, 24.1);
            this->make_raw_handle_ext_delivers_a_valid_handle_impl<vex::core::unmanaged>(42, 24.1);
        }

        TEST_METHOD(make_raw_handle_ext_delivers_a_valid_handle_for_ternary_form)
        {
            this->make_raw_handle_ext_delivers_a_valid_handle_impl
                <vex::core::shared>(42, 24.1, "josephine");
            this->make_raw_handle_ext_delivers_a_valid_handle_impl
                <vex::core::shared_synchronized>(42, 24.1, "josephine");
            this->make_raw_handle_ext_delivers_a_valid_handle_impl
                <vex::core::unique>(42, 24.1, "josephine");
            this->make_raw_handle_ext_delivers_a_valid_handle_impl
                <vex::core::unmanaged>(42, 24.1, "josephine");
        }
    };
}}